#include "glsl.h"
#include <string.h>
GLint pMidValue=-2;
GLint pGrayScale=-2;

static void testError(const char* s){
	GLint error = glGetError();
	if(error==GL_NO_ERROR)
		return;
	fprintf(stderr, "OPENGL ERROR: %s\n", s?s:"");
	if(error&GL_INVALID_ENUM) fprintf(stderr, "GL_INVALID_ENUM\n");
	if(error&GL_INVALID_VALUE) fprintf(stderr, "GL_INVALID_VALUE\n");
	if(error&GL_INVALID_OPERATION) fprintf(stderr, "GL_INVALID_OPERATION\n");
	if(error&GL_STACK_OVERFLOW) fprintf(stderr, "GL_STACK_OVERFLOW\n");
	if(error&GL_STACK_UNDERFLOW) fprintf(stderr, "GL_STACK_UNDERFLOW\n");
	if(error&GL_OUT_OF_MEMORY) fprintf(stderr, "GL_OUT_OF_MEMORY\n");
	if(error&GL_TABLE_TOO_LARGE) fprintf(stderr, "GL_TABLE_TOO_LARGE\n");
}

void GLSLsetShader(){
	GLint i;
	// GLSL vertex shader
/*
	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
	const GLchar *vertexShaderSource[] = {
		"#version 120\n",
		"#pragma debug(on)\n",
		"attribute vec4 gl_Color;",
		"varying vec4 gl_FrontColor; // writable on the vertex shader",
		"varying vec4 gl_Color; // readable on the fragment shader",
		"void main(void){",
		"	gl_FrontColor = gl_Color;",
		"}"
	};
	glShaderSource(vertexShader, sizeof(vertexShaderSource)/sizeof(GLchar*), vertexShaderSource, NULL);
	// free shaderSource if needed
	glCompileShader(vertexShader);
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &i);
	testError("vertex shader");
	if(i!=GL_TRUE){
		glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &i);
		char *s = new char[i];
		glGetShaderInfoLog(vertexShader, i, &i, s);
		fprintf(stderr, "ERROR: vertex shader compile error (%d): \n%s\n", i, i?s:"");
		if(i)
			delete[] s;
		return;
	}
*/

	// GLSL fragment shader, contrast enhancement, 16 bit texture to 8 bit color buffer
	// followed by use of 8 bit colormap
	GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	const GLchar *fragmentShaderSource[] = {
		"#version 120\n",
		"#pragma debug(on)\n",
		"uniform sampler2D tex;",
		"uniform sampler1D colorMap;",
		"uniform float midValue=.5;",
		"uniform float grayScale=1;",
		"void main(void){",
		"	float colorIndex = ( texture2D(tex, gl_TexCoord[0].xy).r - (midValue-.5/grayScale) )*grayScale;",
//		"	gl_FragColor = vec4(colorIndex, colorIndex, colorIndex, 1);", // gray colormap
		"	gl_FragColor = (texture1D(colorMap, colorIndex));",
//		"	gl_FragColor = (texture2D(tex, gl_TexCoord[0].xy)-(midValue-.5/grayScale))*grayScale;",
//		"	gl_FragColor = texture1D(colorMap, gl_TexCoord[0].x+midValue/1000+grayScale/1000);",
		"}"
	};
	glShaderSource(fragmentShader, sizeof(fragmentShaderSource)/sizeof(GLchar*), fragmentShaderSource, 0);
	// free shaderSource if needed
	glCompileShader(fragmentShader);// error: Conditional jump or move depends on uninitialised value(s)
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &i);
	testError("fragment shader");
	if(i!=GL_TRUE){
		glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &i);
		char *s = new char[i];
		glGetShaderInfoLog(fragmentShader, i, &i, s);
		fprintf(stderr, "ERROR: fragment shader compile error (%d): \n%s\n", i, i?s:"");
		if(i)
			delete[] s;
		return;
	}

	// GLSL program
	GLuint program = glCreateProgram();
	glAttachShader(program, fragmentShader);
	glLinkProgram(program);
	glGetProgramiv(program, GL_LINK_STATUS, &i);
	if(i!=GL_TRUE){
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &i);
		char *s = new char[i];
		glGetProgramInfoLog(program, i, &i, s);
		fprintf(stderr, "ERROR: shader program link error: \n%s\n", s);
		return;
	}
	glUseProgram(program);

	pMidValue = glGetUniformLocation(program, "midValue");
	pGrayScale = glGetUniformLocation(program, "grayScale");
	GLuint tex = glGetUniformLocation(program, "tex");
	GLuint colorMap = glGetUniformLocation(program, "colorMap");
	testError("shader");
	if(pMidValue==-1 || pGrayScale==-1){
		// this error will show if the shader program does not use the uniform variables
		fprintf(stderr, "ERROR: retreiving uniform variables from shader failed\n");
		return;
	}
	glUniform1i(tex, 0); // link to GL_TEXTURE0
	glUniform1i(colorMap, 1); // link to GL_TEXTURE1
}

void GLSLsetGray(double midValue, double grayScale){
	if(pMidValue>=0 && pGrayScale>=0){ // not yet or unsuccessfully initialized
		glUniform1f(pMidValue, midValue);
		glUniform1f(pGrayScale, grayScale);
	}
}
